package org.example.hrpc.rpc.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import org.example.hrpc.common.spi.loader.ExtensionLoader;
import org.example.hrpc.rpc.common.Constant;
import org.example.hrpc.rpc.core.Invocation;
import org.example.hrpc.rpc.core.Result;
import org.example.hrpc.rpc.core.RpcInvocation;
import org.example.hrpc.rpc.message.*;
import org.example.hrpc.serializer.Serializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;

/**
 * magic4B|messageType1B|SerializationType1B|requestId8B|responseStatus1B|length4B|body
 */
public class Decoder extends LengthFieldBasedFrameDecoder {
    private final Logger logger = LoggerFactory.getLogger(Decoder.class);

    public final  static Decoder DECODER = new Decoder(65535, 15, 4, -19, 0);

    /*
    maxFrameLength:65535
    lengthFieldOffset:15
    lengthFieldLength:4
    # lengthAdjstment需要调整：因为length是整个包的长度，接下来只需要读取body的长度，所以需要+lengthAdjstment
    lengthAdjstment:-19
    initialBytesToStrip:0,这里设置为0，是因为我们需要序列化类型，响应状态等信息
     */
    public Decoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip) {
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
    }



    /*
    先获取header数据，判断magic，检查消息长度是否大于HeaderLength，然后获取body数据
     */
    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        // 调用LengthFieldBasedFrameDecoder的decode方法解码数据
        Object decode = super.decode(ctx, in);
        logger.info("Decode message object: {}       ", decode);
        if(decode == null) {
            return null;
        }
        // 返回的ByteBuf
        ByteBuf byteBuf = (ByteBuf) decode;
        byte[] header = new byte[Constant.MAGIC_NUMBER.length];
        byteBuf.readBytes(header);
        // magic
        checkMagicNumber(header);
        // messageType
        byte messageType = byteBuf.readByte();
        // serializationType
        byte serializationType = byteBuf.readByte();
        // requestId
        long requestId = byteBuf.readLong();
        // responseStatus
        byte serviceStatus = byteBuf.readByte();
        // length
        int length = byteBuf.readInt();
        // doby消息体长度
        int bodyLength = length - Constant.TOTAL_LENGTH;
        logger.info("magic: {}, messageType: {}, serializationType: {}, requestId: {}, " +
                "serviceStatus: {}, length: {}", header, messageType, serializationType, requestId, serviceStatus, length);
        Object obj = null;
        MessageType messageTypeEmum = MessageType.getType(messageType);
        if(MessageType.SERVICE_REQUEST.equals(messageTypeEmum) ||
                MessageType.HEART_BEAT_RQEUEST.equals(messageTypeEmum)) {
            Request request = new Request();
            request.setMessageType(messageTypeEmum);
            request.setSerializationType(SerializationType.getType(serializationType));
            request.setRequestId(requestId);
            obj = request;
        } else if(MessageType.SERVICE_RESPONSE.equals(messageTypeEmum) ||
                MessageType.HEART_BEAT_RESPONSE.equals(messageTypeEmum)) {
            Response response = new Response();
            response.setRequestId(requestId);
            response.setMessageType(messageTypeEmum);
            response.setServiceStatus(ServiceStatus.getType(serviceStatus));
            response.setSerializationType(SerializationType.getType(serializationType));
            obj = response;
        }
        if(bodyLength > 0) {
            byte[] data = new byte[bodyLength];
            byteBuf.readBytes(data);
            Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class)
                    .getExtension(SerializationType.getType(serializationType).getName());
            if(MessageType.SERVICE_REQUEST.equals(messageTypeEmum)) {
                Invocation invocation = serializer.deserialize(data, Invocation.class);
                Request request = (Request) obj;
                request.setData(invocation);
            } else if(MessageType.SERVICE_RESPONSE.equals(messageTypeEmum)) {
                Result result = serializer.deserialize(data, Result.class);
                Response response = (Response) obj;
                response.setResult(result);
            }
        }
        // logger
        if(MessageType.SERVICE_REQUEST.equals(messageTypeEmum) ||
                MessageType.HEART_BEAT_RQEUEST.equals(messageTypeEmum)) {
            logger.info("Decode Request Message: {}", (Request)obj);
        } else if(MessageType.SERVICE_RESPONSE.equals(messageTypeEmum) ||
                MessageType.HEART_BEAT_RESPONSE.equals(messageTypeEmum)) {
            logger.info("Decode Response Message: {}", (Response)obj);
        }
        return obj;
    }

    private void checkMagicNumber(byte[] header) {
        int magicNumberLength = Constant.MAGIC_NUMBER.length;
        for(int i=0;i<magicNumberLength;i++){
            if(header[i] != Constant.MAGIC_NUMBER[i]) {
                throw new IllegalArgumentException(String.format("Invalid magic code: %s", Arrays.toString(header)));
            }
        }
    }

}
